use std::collections::HashMap;
use std::ops::Add;

use chrono;
use anyhow::Result;
use hyper::Client;

use crate::cmd::Output;
use crate::cmd::common;
use crate::miner;
use crate::data as db;
use crate::miner::types::TaskType;
use crate::miner::worker::SealWorkerStats;
use crate::data::CommonWorkerLimit;

pub async fn pledge_sector(run: bool) -> Result<Option<Box<dyn Output>>> {
    let pool = &common::parse_db().await?;
    let client = &Client::new();
    let (url, token) = &common::parse_miner()?;

    let mut miner = 1000u64;
    let config: Vec<CommonWorkerLimit> = db::query_worker_task_limit(pool).await?;
    let mut worker_config = HashMap::<String, i32>::new();
    for w in config {
        worker_config.insert(w.worker_id, w.task_limit);
    }
    let workers = miner::query_worker_states(client, url, token).await?;
    if workers.is_empty(){
        log::debug!("no worker running with miner.")
    }
    let all_jobs = miner::jobs::query_jobs_from_miner(client, &url, &token).await?;

    let mut pledge_workers = Vec::<SealWorkerStats>::new();
    for (id, worker) in workers {
        let host = worker.info.hostname.clone();
        let width = 10usize;
        if !worker.enabled {
            log::debug!("worker {holder:<width$} is disable.", holder = host, width = width);
            continue
        }

        let limit = worker_config.get(&id);
        if limit.is_none() {
            log::debug!("worker {holder:<width$}  not support new PC1.", holder = host, width = width);
            continue
        }

        let limit = limit.unwrap();

        let worker_info = db::query_worker_info_with_id(pool, id.as_str()).await?;
        if worker_info.is_none() {
            log::debug!("worker {holder:<width$}  not found in db.", holder = host, width = width);
            continue
        }
        let worker_info = worker_info.unwrap();
        if worker_info.accept_new != 1 {
            log::debug!("worker {holder:<width$}  can not accept new task.", holder = host, width = width);
            continue
        }

        let update_time = worker_info.update_time.add(chrono::Duration::minutes(5));
        let now = chrono::Local::now().naive_local();
        if now.lt(&update_time){
            log::debug!("worker {holder:<width$}  is now register, wait for 5 minute", holder = host, width = width);
            continue
        }

        let count = db::query_worker_pc1_sector_count(pool, &worker.info.hostname).await?;
        if count as i32 >= *limit {
            log::debug!("worker {holder:<width$} limit {limit}, running {count}", holder = host, width = width, limit = limit, count = count);
            continue
        }

        let mut running = 0;
        let mut assigned = 0;
        let mut window = 0;

        let jobs = all_jobs.get(&id);
        if jobs.is_some(){
            let jobs = jobs.unwrap();
            if jobs.len() > 0 {
                for j in jobs {
                    if miner == 0 {
                        miner = j.sector.miner
                    }
                    let f = match j.task {
                        TaskType::AddPiece => true,
                        TaskType::PreCommit1 => true,
                        _ => false,
                    };
                    if f {
                        if j.run_wait > 0 {
                            assigned = assigned + 1;
                            if j.run_wait > window {
                                window = j.run_wait
                            }
                        }
                        if j.run_wait == 0 {
                            running = running + 1;
                        }
                    }
                }
            }
        }

        // println!("worker {}, limit {}, running {}, assigned {}, window {}", id, limit, running, assigned, window);
        if assigned > 0 {
            log::debug!("worker {holder:<width$}  have assigned task {assign}", holder = host, width = width, assign = assigned);
            continue
        }

        if running >= *limit {
            log::debug!("worker {holder:<width$}  have enough task running {running}, limit {limit}", holder = host, width = width, running = running, limit = limit);
            continue
        }

        pledge_workers.push(worker);
    }

    if pledge_workers.is_empty(){
        log::debug!("no worker can handle new more task.")
    } else {
        log::debug!("pledge length {}", pledge_workers.len());
    }
    let length = pledge_workers.len();

    if run {
        for worker in pledge_workers {
            let sector = miner::pledge::pledge_on_sector(client, url, token).await?;
            let _ = db::pledge_sector_for_worker(pool, &worker.info.hostname, sector.number, sector.miner).await?;
            log::info!("pledge sector {} => {}", sector.number, &worker.info.hostname);
        }
    }

    log::info!("{} sector pledge", length);
    Ok(Option::None)
}